package self.zhangkang.chart.service.index

import self.zhangkang.chart.data.KDJ
import self.zhangkang.chart.data.KLineInfo

/**
 * @author : Android-张康
 * created on: 2023/5/9 09:44
 * description: 计算股票的 KDJ
 */
class KDJTools(private val X: Int, private val Y: Int, private val Z: Int) {


    companion object {

        //KDJ参数
        private const val KDJN = 9
        private const val KDJM1 = 3
        private const val KDJM2 = 3

        fun calculationKDJ(dataList: MutableList<KLineInfo>) {
            val kdjTools = KDJTools(KDJN, KDJM1, KDJM2)
            var previousKDJ: KDJ? = null
            dataList.forEachIndexed { index, kLineInfo ->
                val kdj = kdjTools.calculationKDJ(
                    data = dataList,
                    currentIndex = index,
                    previousKDJ = previousKDJ,
                    closePrice = kLineInfo.closePrice,
                    getMaxPrice = {
                        it.highPrice
                    },
                    getMinPrice = {
                        it.lowerPrice
                    }
                )
                dataList[index] = kLineInfo.copy(kdj = kdj)
                previousKDJ = kdj
            }
        }

    }

    private val defaultK = 50F
    private val defaultD = 50F

    /**
     * 计算KDJ
     *
     * @param data 当前的数据
     * @param currentIndex 当前数据
     * @param previousKDJ 上一个周期的KDJ值
     * @param closePrice 收盘价
     * @param getMaxPrice 获取最大值
     * @param getMinPrice 获取最小值
     */
    fun <T> calculationKDJ(
        data: List<T>,
        currentIndex: Int,
        previousKDJ: KDJ?,
        closePrice: Float,
        getMaxPrice: (T) -> Float,
        getMinPrice: (T) -> Float
    ): KDJ {
        var maxPrice = Float.MIN_VALUE
        var minPrice = Float.MAX_VALUE
        val end = currentIndex
        var start = end - X + 1
        if (start < 0) {
            start = 0
        }
        for (index in start..end) {
            val currentData = data[index]
            maxPrice = maxOf(maxPrice, getMaxPrice(currentData))
            minPrice = minOf(minPrice, getMinPrice(currentData))
        }
        return calculationKDJ(closePrice, maxPrice, minPrice, previousKDJ)
    }

    /**
     * 计算KDJ
     *
     * @param closePrice 收盘价
     * @param maxPrice [X] 日内最高价
     * @param minPrice [X] 日内最低价
     * @param previousKDJ 上一个周期的KDJ值
     */
    private fun calculationKDJ(
        closePrice: Float,
        maxPrice: Float,
        minPrice: Float,
        previousKDJ: KDJ?
    ): KDJ {
        val RSV = RSV(closePrice, maxPrice, minPrice)
        //异常情况K和D值的范围一般为0-100，部分特殊行情可能会超出这个范围，特别是新股刚上市的几天；
        // 基于“钝化现象”，我们规定：当K值和D值计算结果大于100.00时，显示为100.00；
        // 当K值和D值计算结果小于0.00时，显示为0.00；
        // J值不做任何限制，它可能是个很大的数值，也可能是个负数；
        val K = K(previousKDJ?.K ?: defaultK, RSV)
        val D = D(previousKDJ?.D ?: defaultD, K)
        val J = J(K, D)
        return KDJ(
            K = minOf(100F, maxOf(K, 0F)),
            D = minOf(100F, maxOf(D, 0F)),
            J = minOf(100F, maxOf(J, 0F))
        )
    }

    /**
     * 计算RSV未成熟随机值
     * RSV = （收盘价 - 最低价） / （最高价 - 最低价） * 100
     * 1、其中，收盘价、最低价、最高价的取值范围为X周期内；
     * 2、如，当X=9时，即“当日+前移8个交易日”共9个交易日中的收盘价、最低价、最高价；
     * 3、当日未出收盘价前，将最新价视为收盘价，代入公式计算；
     * 4、向前取值时，若数据不足9个交易日，亦不影响计算（如，新股刚上市交易5天，我们要计算第五天的RSV，但数据不足9天，则取5天哪的收盘价、最低价、最高价进行计算即可）
     */
    private fun RSV(closePrice: Float, maxPrice: Float, minPrice: Float): Float {
        val diff = maxPrice - minPrice
        if (diff > 0 || diff < 0) {
            return (closePrice - minPrice) / diff * 100
        }
        return 0F
    }

    /**
     * 计算K值
     * K值 = [（Y-1）/ Y ] * 前一周期K值 + （1 / Y）* 当周期RSV值
     * 若无前一日K值与D值，则一般分别用50代替。
     */
    private fun K(previousK: Float, RSV: Float): Float {
        return (Y - 1.0F) / Y * previousK + 1.0F / Y * RSV
    }

    /**
     * 计算D值
     * D值 = [（Z-1）/ Y ] * 前一周期D值 + （1 / Z）* 当周期K值
     * 若无前一日K值与D值，则一般分别用50代替。
     */
    private fun D(previousD: Float, K: Float): Float {
        return (Z - 1.0F) / Y * previousD + 1.0F / Z * K
    }

    /**
     * 计算J值
     * J值 =3 * K值 - 2*d值
     */
    private fun J(K: Float, D: Float): Float {
        return 3 * K - 2 * D
    }
}